package ua.kpi_java_training.zastupaylo.module1;

import java.util.ArrayList;
import java.util.Scanner;

/**
 * This class includes methods for long division.
 *
 * @author Zastupaylo Alexander (zastupaylo@gmail.com)
 * @version 1.4, 25/05/2013
 */
public class Division {

    private static final int INTERMEDIATE_VALUES_PER_STEP = 2;
    private static final int BASE = 10;

    public static int dividend = 0;
    public static int divisor = 1;
    public static int quotient = 0;
    public static boolean positiveSign = true;
    //Instead of this ArrayList we can use String and method getNumberValue(), but I don't know what is better,
    //because of cast in this method.Can change ArrayList to String, if necessary.
    public static ArrayList<Integer> dividendDigits = new ArrayList<Integer>();

    public static Scanner scanner = new Scanner(System.in);

    /**
     * @param args - Not used
     */
    public static void main(String[] args) {
        input();
        isPositiveSign();
        setPositiveValues();
        convertDividendToDigits(dividend);
        output(makeDivision());
    }

    /**
     * Read dividend and divisor values from the command line.
     * Input values should be <code>int</code> type.
     */
    public static void input(){
        dividend = inputNumber("Enter dividend: ");
        while(true){
            divisor = inputNumber("Enter divisor: ");
            if (divisor != 0){
                break;
            }
            System.out.println("Error. Divisor can't be 0. Repeat input.");
        }
    }

    /**
     * Read number from the command line.
     *
     * @param numberName - hint for input
     * @return inputted number
     */
    public static int inputNumber(String numberName) {
        System.out.println(numberName);
        while (!scanner.hasNextInt()) {
            System.out.println("Invalid input. Enter number, please.");
            scanner.next();
        }
        return scanner.nextInt();
    }

    /**
     * <code>Division</code>.positiveSign sets to <code>true</code>,
     * if sign of quotient should be positive, otherwise - <code>false</code>.
     */
    public static void isPositiveSign() {
        if ((dividend < 0 && divisor > 0) || (dividend > 0 && divisor < 0)) {
            positiveSign = false;
        }
    }

    /**
     * Set <code>Division</>.dividend and <code>Division</>.divisor values to positive.
     */
    public static void setPositiveValues() {
        dividend = Math.abs(dividend);
        divisor = Math.abs(divisor);
    }

    /**
     * Convert number to <code>ArrayList</code> of digits.
     * Result is written to <code>Division</code>.dividendDigits.
     *
     * @see ArrayList
     */
    public static void convertDividendToDigits(int dividend){
        if (dividend == 0){
            dividendDigits.add(0);
        }
        else{
            while(dividend > 0) {
                dividendDigits.add(0, dividend % BASE);
                dividend /= BASE;
            }
        }
    }

    /**
     * Print line of '-' to the command line.
     * Line length equals to digits quantity in number.
     *
     * @param number
     */
    public static void drawLine(int number){
        System.out.printf(String.format("%0" + getNumberLength(number) + "d\n", 0).replace("0","-"));
    }

    /**
     * Get quantity of digits in number.
     *
     * @param number
     * @return quantity of digits in number
     */
    public static int getNumberLength(int number) {
        return String.valueOf(number).length();
    }

    /**
     * Print n backspaces to command line.
     *
     * @param n - quantity of the backspaces
     */
    public static void printBackspaces(int n) {
        System.out.printf((n == 0) ? "" : String.format("%" + n + "s", ""));
    }

    /**
     * Make division of <code>Division</code>.dividend and <code>Division</code>.divisor
     *
     * @return intermediate values, holds intermediate values of division process.
     * @see ArrayList
     */
    public static ArrayList<Integer> makeDivision() {
        //Haven't decided how to rename variable to more intuitive. divisionValues?
        ArrayList<Integer> intermediateValues = new ArrayList<Integer>();
        int currDividend = dividendDigits.remove(0);
        while (true) {
            if (currDividend >= divisor) {
                currDividend = divide(intermediateValues, currDividend);
            } else if (!dividendDigits.isEmpty()) {
                currDividend = addDigitToCurrDividend(currDividend);
                checkAddingZeroToQuotient(currDividend);

            } else {
                //Add reminder of division and stop division process
                intermediateValues.add(currDividend);
                break;
            }
        }
        return intermediateValues;
    }

    /**
     * Divide currDividend and <code>Division</code>.divisor.
     * Result of division add to <code>Division</code>.quotient.
     *
     * @param intermediateValues - intermediate values of division process
     * @param currDividend - current dividend value
     * @return remainder of division
     */
    public static int divide(ArrayList<Integer> intermediateValues, int currDividend) {
        intermediateValues.add(currDividend);
        quotient = quotient * BASE + currDividend / divisor;
        intermediateValues.add(currDividend / divisor * divisor);

        return currDividend % divisor;
    }

    /**
     * @param currDividend - current dividend value
     * @return - filled with one digit current dividend value
     */
    public static int addDigitToCurrDividend(int currDividend) {
        return Integer.parseInt(Integer.toString(currDividend) + Integer.toString(dividendDigits.remove(0)));
    }

    /**
     * Check adding zero to quotient because of high order in dividend.
     * Add zero to quotient, if needed.
     *
     * @param currDividend - current dividend value
     */
    public static void checkAddingZeroToQuotient(int currDividend) {
        //condition quotient != 0 is needed only for first divide iteration
        if (quotient != 0 && currDividend < divisor) {
            quotient *= BASE;
        }
    }

    /**
     * Print division initial conditions to the command line.
     */
    public static void  printHeader(){
        System.out.printf("\n%d | %d\n", dividend, divisor);
    }

    /**
     * Print division process on each step.
     *
     * @param intermediateValues - intermediate values of dividing <code>Division</code>.dividend
     *                           and <code>Division</code>.divisor.
     * @see ArrayList
     */
    public static void printIntermediateValues(ArrayList<Integer> intermediateValues){
        for(int count = 1; count < intermediateValues.size(); count++){
            printBackspaces(count / INTERMEDIATE_VALUES_PER_STEP);
            System.out.println(intermediateValues.get(count));
            if(count % 2 == 1){
                printBackspaces(count / INTERMEDIATE_VALUES_PER_STEP);
                drawLine(intermediateValues.get(count));
            }
        }
    }

    /**
     * Print <code>Division</code>.quotient value.
     *
     * @see ArrayList
     */
    public static void printQuotient(){
        System.out.printf(positiveSign? "%s %d":"%s -%d", "Result of dividing:", quotient);
    }

    /**
     * Print all process of dividing and result.
     * Wrapping method of printHeader(), printIntermediateValues() and printQuotient().
     *
     * @param intermediateValues - intermediate values of dividing.
     * @see ArrayList
     */
    public static void output(ArrayList<Integer> intermediateValues){
        printHeader();
        printIntermediateValues(intermediateValues);
        printQuotient();
    }
}
